package com.ly.demo.redisTemplate;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.junit.platform.commons.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 包    名 ：com.xxcg.platform.system.util
 * 文 件 名 : RedisUtil
 * 描    述 : redis工具类
 * 作    者 ：zhaow
 * 创建时间 ：2018/10/13 0013 11:09
 * 版    本 ：1.0
 */
@Component
@Slf4j
@Data
public class RedisUtil {
    @Autowired private RedisTemplate redisTemplate;

    /*******************************bitmap操作***********************************/
    /**
     * setbit 设置一个值
     *
     * @param key
     * @param offset
     * @param value
     */
    public void setBit(String key, Long offset, Boolean value) {
        if (StringUtils.isBlank(key)) {
            throw new IllegalArgumentException("redis key is null");
        }
        redisTemplate.execute((RedisCallback<Boolean>)con -> con.setBit(key.getBytes(), offset, value));
    }

    /**
     * getBit
     * 获取指定offset的值
     *
     * @param key
     * @param offset
     * @return
     */

    public Boolean getBit(String key, Integer offset) {
        if (StringUtils.isBlank(key)) {
            throw new IllegalArgumentException("redis key is null");
        }
        return (Boolean)redisTemplate.execute((RedisCallback<Boolean>)con -> con.getBit(key.getBytes(), offset));
    }

    public <T> T executeLua(String classPathResource, Class<T> t, List<String> keys, Object... args) {
        DefaultRedisScript defaultRedisScript = new DefaultRedisScript<List>();
        defaultRedisScript.setResultType(t);
        defaultRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource(classPathResource)));

        return null;
    }

    public void removeBeforeAndAfter(String key) {
        Set<String> keys = redisTemplate.keys("*" + key + "*");
        redisTemplate.delete(keys);
    }

    /**
     * 对一个 key-value 的值进行加减操作,
     * 如果该 key 不存在 将创建一个key 并赋值该 number
     * 如果 key 存在,但 value 不是 纯数字 ,将报错
     *
     * @param key
     * @param number
     */
    public Double increment(String key, double number) {
        Double temp = redisTemplate.opsForValue().increment(key, number);
        return temp;
    }

    public Long incr(String key) {
        Long temp = redisTemplate.opsForValue().increment(key, 1);
        return temp;
    }

    public Long decr(String key) {
        Long temp = redisTemplate.opsForValue().increment(key, -1);
        return temp;
    }

    /**
     * 给一个指定的 key 值附加过期时间
     *
     * @param key
     * @param time
     * @param timeUnit
     * @return
     */

    public boolean expire(String key, long time, TimeUnit timeUnit) {
        return redisTemplate.boundValueOps(key).expire(time, timeUnit);
    }
    //

    /**
     * 写入缓存
     *
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, Object value) {
        boolean result = false;
        try {
            ValueOperations<String, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
    //

    /**
     * 如果 key 存在则覆盖,并返回旧值.
     * 如果不存在,返回null 并添加
     *
     * @param key
     * @param value
     * @return
     */
    public String getAndSet(String key, String value) {
        return (String)redisTemplate.opsForValue().getAndSet(key, value);
    }

    public void removeMore(String key) {
        Set<String> keys = redisTemplate.keys(key + "*");
        redisTemplate.delete(keys);
    }

    /**
     * 写入缓存设置时效时间
     *
     * @param key
     * @param value
     * @return
     */
    public boolean set(final String key, Object value, Long expireTime, TimeUnit timeUnit) {
        boolean result = false;
        try {
            ValueOperations<String, Object> operations = redisTemplate.opsForValue();
            operations.set(key, value);
            redisTemplate.expire(key, expireTime, timeUnit);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 批量删除对应的value
     *
     * @param keys 键集合
     */
    public void remove(Collection<String> keys) {
        if (Objects.nonNull(keys) && !keys.isEmpty()) {
            redisTemplate.delete(keys);
        }
    }

    /**
     * 批量删除对应的value
     *
     * @param keys
     */
    public void remove(final String... keys) {
        for (String key : keys) {
            remove(key);
        }
    }

    /**
     * 删除对应的value
     *
     * @param key
     */
    public boolean remove(final String key) {
        return redisTemplate.delete(key);
    }

    /**
     * 判断缓存中是否有对应的value
     *
     * @param key
     * @return
     */
    public boolean exists(final String key) {
        return redisTemplate.hasKey(key);
    }
    //

    /**
     * 读取缓存
     *
     * @param key
     * @return
     */
    public Object get(final String key) {
        Object result = null;
        ValueOperations<String, Object> operations = redisTemplate.opsForValue();
        result = operations.get(key);
        return result;
    }

    /**
     * 哈希 添加
     *
     * @param key
     * @param hashKey
     * @param value
     */
    public void hmSet(String key, Object hashKey, Object value) {
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        hash.put(key, hashKey, value);
    }

    /**
     * 获取所有给定字段的值
     *
     * @param key
     * @return
     */
    public Map<Object, Object> hGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    /**
     * 哈希获取数据
     *
     * @param key
     * @param hashKey
     * @return
     */
    public Object hmGet(String key, Object hashKey) {
        HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
        return hash.get(key, hashKey);
    }

    /**
     * 验证指定 key 下 有没有指定的 hashkey
     *
     * @param key
     * @param hashKey
     * @return
     */
    public boolean hashKey(String key, String hashKey) {
        return redisTemplate.opsForHash().hasKey(key, hashKey);
    }

    /**
     * 获取指定 hash 下面的 键值对 数量 
     *
     * @param key
     * @return
     */
    public Long hashSize(String key) {
        return redisTemplate.opsForHash().size(key);
    }

    /**
     * 给哈希表key中的指定字段的整数值加上增量increment
     *
     * @param key
     * @param field
     * @param increment
     * @return
     */
    public Long hashIncrBy(String key, Object field, long increment) {
        return redisTemplate.opsForHash().increment(key, field, increment);
    }

    /**
     * 删除指定 hash 的 HashKey
     *
     * @param key
     * @param hashKeys
     * @return 删除成功的 数量
     */
    public long delete(String key, String... hashKeys) {
        return redisTemplate.opsForHash().delete(key, hashKeys);
    }

    public String encodeKey(String key) {
        Assert.notNull(key);
        return key.replace(" ", "").replace("@", "-");
    }

    public void removeObject(String key) {
        Assert.notNull(key);
        key = encodeKey(key);
        redisTemplate.delete(key);
    }

    /**
     * 列表添加
     *
     * @param k
     * @param v
     */
    public void lPush(String k, Object v) {
        ListOperations<String, Object> list = redisTemplate.opsForList();
        list.rightPush(k, v);
    }

    /**
     * 列表删除
     *
     * @param k
     */
    public Object lPop(String k) {
        ListOperations<String, Object> list = redisTemplate.opsForList();
        return list.leftPop(k);
    }

    /**
     * 移除N个值为value
     *
     * @param key   键
     * @param count 移除多少个
     * @param value 值
     * @return 移除的个数
     */
    public long lRemove(String key, long count, Object value) {
        Long remove = redisTemplate.opsForList().remove(key, count, value);
        return remove;
    }

    /**
     * 列表获取
     *
     * @param k
     * @param l
     * @param l1
     * @return
     */
    public List<Object> lRange(String k, long l, long l1) {
        ListOperations<String, Object> list = redisTemplate.opsForList();
        return list.range(k, l, l1);
    }

    /**
     * 获取列表长度
     *
     * @param k
     * @return
     */
    public Long lSize(String k) {
        ListOperations<String, Object> list = redisTemplate.opsForList();
        return list.size(k);
    }

    /**
     * 集合添加
     *
     * @param key
     * @param value
     */
    public void add(String key, Object value) {
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        set.add(key, value);
    }

    /**
     * 集合获取
     *
     * @param key
     * @return
     */
    public Set<Object> setMembers(String key) {
        SetOperations<String, Object> set = redisTemplate.opsForSet();
        return set.members(key);
    }

    /**
     * 有序集合添加
     *
     * @param key
     * @param value
     * @param scoure
     */
    public void zAdd(String key, Object value, double scoure) {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        zset.add(key, value, scoure);
    }

    /**
     * 使用加法操作分数
     */
    public void incrementScore(String key, Object value, Double score) {
        redisTemplate.opsForZSet().incrementScore(key, value, score);
    }

    /**
     * 有序集合获取
     *
     * @param key
     * @param scoure
     * @param scoure1
     * @return
     */
    public Set<Object> rangeByScore(String key, double scoure, double scoure1) {
        ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
        return zset.rangeByScore(key, scoure, scoure1);
    }

    /**
     * 有序集合获取score
     *
     * @param key
     * @param value
     * @return
     */
    public Double score(String key, Object value) {
        return redisTemplate.opsForZSet().score(key, value);
    }

    /**
     * 获取索引区间内的排序结果集合(从0开始,从大到小,带上分数)
     *
     * @param key
     * @param start
     * @param end
     * @return
     */
    public Set<Object> reverseRange(String key, long start, long end) {
        return redisTemplate.opsForZSet().reverseRange(key, start, end);
    }

    /**
     * SCAN批量删除
     * <p>SCAN命令用于迭代当前数据库中的key集合</p>
     *
     * @param pattern 匹配表达式
     * @return
     */
    public Long removePattern(String pattern) {
        ScanOptions options = ScanOptions.scanOptions().count(100L).match(pattern).build();
        RedisSerializer<String> redisSerializer = (RedisSerializer<String>)redisTemplate.getKeySerializer();
        Cursor cursor = (Cursor)redisTemplate.executeWithStickyConnection(
            redisConnection -> new ConvertingCursor<>(redisConnection.scan(options), redisSerializer::deserialize));
        Set<String> keys = new HashSet<>();
        while (cursor.hasNext()) {
            keys.add(cursor.next().toString());
        }
        try {
            cursor.close();
        } catch (IOException e) {
            log.error("关闭cursor失败", e);
        }
        if (!CollectionUtils.isEmpty(keys)) {
            return redisTemplate.delete(keys);
        }
        return 0L;
    }

    /**
     * HSCAN批量删除
     * <p>HSCAN命令用于迭代Hash类型中的键值对</p>
     *
     * @param key     哈希key
     * @param pattern filed匹配表达式
     */
    public Long removeHashPattern(String key, String pattern) {
        ScanOptions options = ScanOptions.scanOptions().match(pattern).count(100L).build();
        Cursor<Map.Entry<String, Object>> cursor = redisTemplate.opsForHash().scan(key, options);
        Set<String> keys = new HashSet<>();
        while (cursor.hasNext()) {
            Map.Entry<String, Object> curr = cursor.next();
            keys.add(curr.getKey());
        }
        try {
            cursor.close();
        } catch (IOException e) {
            log.error("关闭cursor失败", e);
        }
        if (!CollectionUtils.isEmpty(keys)) {
            return redisTemplate.opsForHash().delete(key, keys.toArray(new String[0]));
        }
        return 0L;
    }

    /**
     * SSCAN批量删除
     * <p>SSCAN命令用于迭代SET集合中的元素</p>
     *
     * @param key     键
     * @param pattern 匹配表达式
     * @return
     */
    public Long removeSetPattern(String key, String pattern) {
        ScanOptions options = ScanOptions.scanOptions().match(pattern).count(100L).build();
        Cursor cursor = redisTemplate.opsForSet().scan(key, options);
        Set<Object> members = new HashSet<>();
        while (cursor.hasNext()) {
            members.add(cursor.next());
        }
        try {
            cursor.close();
        } catch (IOException e) {
            log.error("关闭cursor失败", e);
        }
        if (!CollectionUtils.isEmpty(members)) {
            return redisTemplate.opsForSet().remove(key, members.toArray(new Object[0]));
        }
        return 0L;
    }

    /**
     * ZSCAN批量删除
     * <p>ZSCAN 命令用于迭代SortSet集合中的元素和元素对应的分值</p>
     *
     * @param key     键
     * @param pattern 匹配表达式
     * @return
     */
    public Long removeZSetPattern(String key, String pattern) {
        //zset value序列化特殊处理,否则取不到值
        RedisSerializer<Object> valueSerializer = (RedisSerializer<Object>)redisTemplate.getValueSerializer();
        String changeKey = new String(valueSerializer.serialize(pattern));
        ScanOptions options = ScanOptions.scanOptions().match(changeKey).count(100L).build();
        Cursor<ZSetOperations.TypedTuple<Object>> cursor = redisTemplate.opsForZSet().scan(key, options);
        Set<Object> members = new HashSet<>();
        while (cursor.hasNext()) {
            ZSetOperations.TypedTuple curr = cursor.next();
            members.add(curr.getValue());
        }
        try {
            cursor.close();
        } catch (IOException e) {
            log.error("关闭cursor失败", e);
        }
        if (!CollectionUtils.isEmpty(members)) {
            return redisTemplate.opsForZSet().remove(key, members.toArray(new Object[0]));
        }
        return 0L;
    }

    /**
     * 读取模糊Key
     *
     * @param key
     * @return
     */
    public Set<String> getKeysPattern(final String key) {
        return redisTemplate.keys(key);
    }

}

